/**************************************
 * Created by xruntime
 * GNU Lesser General Public License
***************************************/

#ifndef FRACTION_H_
#define FRACTION_H_

template <typename T = int> 
class fraction
{
private:
	T numerator;
	T denominator;	
	template <typename U>
	friend fraction<U> operator+(fraction<U> a, fraction<U> b);
	template <typename U>
	friend fraction<U> operator-(fraction<U> a, fraction<U> b);
	template <typename U>
	friend fraction<U> operator*(const fraction<U>& a, const fraction<U>& b);
	template <typename U>
	friend fraction<U> operator/(const fraction<U>& a, const fraction<U>& b);
	template <typename U>
	friend bool operator==(fraction<U> a, fraction<U> b);
	template <typename U>
	friend bool operator>(const fraction<U>& a, const fraction<U>& b);
	template <typename U> 
	friend bool operator<(const fraction<U>& a, const fraction<U>& b);
	template <typename U> 
	friend bool operator>=(const fraction<U>& a, const fraction<U>& b);
	template <typename U> 
	friend bool operator<=(const fraction<U>& a, const fraction<U>& b);
	template <typename U> 
	friend bool operator!=(fraction<U> a, fraction<U> b);
public:
	void simplify();
	T getnum() const;
	T getdenom() const; 
	bool isequal(const T num, const T denom) const;
	void set(const T num = 0, const T denom = 1, const bool reduce = true);
	fraction(const T num = 0, const T denom = 1, const bool reduce = true);
};

template <typename T>
fraction<T>::fraction(const T num, const T denom, const bool reduce)
{
	numerator = num;
	denominator = denom; 
	if (reduce)
	{
		simplify();
	}
}

template <typename T>
void fraction<T>::simplify()
{
	int fgcd;
	while ((fgcd = gcd(numerator, denominator)) != 1)
	{
		numerator /= fgcd; 
		denominator /= fgcd;
	}	
}

template <typename T>
T fraction<T>::getnum() const
{
	return numerator;
}

template <typename T>
T fraction<T>::getdenom() const
{
	return denominator;
}

template <typename T>
void fraction<T>::set(const T num, const T denom, const bool reduce)
{
	numerator = num;
	denominator = denom; 
	if (reduce)
	{
		simplify();
	}
}

template <typename T>
bool fraction<T>::isequal(const T num, const T denom) const
{
	fraction<T> temp(num, denom); 
	temp.simplify(); 
	return (numerator == temp.getnum() && denominator == temp.getdenom()); 
}

template <typename U>
fraction<U> operator*(const fraction<U>& a, const fraction<U>& b)
{
	fraction<U> retfrac; 
	retfrac.numerator = a.numerator * b.numerator; 
	retfrac.denominator = a.denominator * b.denominator; 
	retfrac.simplify();
	return retfrac; 
}

template <typename U>
fraction<U> operator/(const fraction<U>& a, const fraction<U>& b)
{
	fraction<U> retfrac; 
	retfrac.numerator = a.numerator * b.denominator; 
	retfrac.denominator = a.denominator * b.numerator; 
	retfrac.simplify();
	return retfrac; 
}

template <typename U>
fraction<U> operator+(fraction<U> a, fraction<U> b)
{
	fraction<U> retfrac; 
	if (a.denominator != b.denominator)
	{
		int adenom = a.denominator; 
		a.numerator *= b.denominator;		
		a.denominator *= b.denominator; 		
		b.numerator *= adenom;
		b.denominator *= adenom;
	}	
	retfrac.numerator = a.numerator + b.numerator;
	retfrac.denominator = a.denominator; 
	retfrac.simplify(); 
	return retfrac;
}

template <typename U>
fraction<U> operator-(fraction<U> a, fraction<U> b)
{
	fraction<U> retfrac; 
	if (a.denominator != b.denominator)
	{
		int adenom = a.denominator; 
		a.numerator *= b.denominator;		
		a.denominator *= b.denominator; 		
		b.numerator *= adenom;
		b.denominator *= adenom;
	}	
	retfrac.numerator = a.numerator - b.numerator;
	retfrac.denominator = a.denominator; 
	retfrac.simplify();
	return retfrac;
}

template <typename U>
bool operator==(fraction<U> a, fraction<U> b)
{
	a.simplify();
	b.simplify();
	return (a.numerator == b.numerator && a.denominator == b.denominator);
}

template <typename U> 
bool operator>(const fraction<U> &a, const fraction<U>& b)
{
	return ((a-b).getnum() > 0);
}

template <typename U> 
bool operator<(const fraction<U>& a, const fraction<U>& b)
{
	return !((a-b).getnum() > 0);
}

template <typename U> 
bool operator>=(const fraction<U>& a, const fraction<U>& b)
{
	return ((a == b) || (a > b));
}

template <typename U> 
bool operator<=(const fraction<U>& a, const fraction<U>& b)
{
	return ((a == b) || (a < b));
}

template <typename U> 
bool operator!=(fraction<U> a, fraction<U> b)
{
	a.simplify();
	b.simplify();
	return !(a.numerator == b.numerator && a.denominator == b.denominator);
}

template <typename U>
std::ostream& operator<<(std::ostream& os, fraction<U> const& frac)
{
	return os << frac.getnum() << "/" << frac.getdenom(); 
}

int gcd(const int& a, const int& b)
{
	if (b == 0) return a; 
	else return gcd(b, a % b);		
}

#endif /*FRACTION_H_*/
